/*
 * @Author jiangzhiwei
 * @Date 2023-07-19 14:26:34
 * @LastEditors jiangzhiwei
 * @LastEditTime 2023-07-21 17:49:15
 * @FilePath \test-v3\src\components\yc-cron\utils\domutil.js
 * @Description 结点工具类
 */
import { isVue2, isVue3, h as h2 } from 'vue-demi'

/** 封装createElement */
export function h (...args) {
  const argsN = [...args]
  handleComponentChildren(argsN)
  handleAttrAndSlot(argsN)
  return h2(...argsN)
}

/** 处理vue2中子结点需要是数组的问题，以及vue3中组件的子元素需要使用插槽传递的问题 */
function handleComponentChildren (args) {
  if (isVue2) {
    const args1 = args[1]
    if (args1 && args1.context !== undefined) { // 第二个参数是个单独结点。这里只有结点里才有context属性
      args[1] = [args1]
    }
    const args2 = args[2]
    if (args2 && !Array.isArray(args2)) { // 第三个参数不是数组
      args[2] = [args2]
    }
  } else {
    if (typeof args[0] !== 'string') { // 标签名不是一个字符串，即为一个组件
      const args1 = args[1]
      if (isVNodeOrVNodes(args1)) { // 第二个参数是结点或结点数组
        args[1] = () => args1 // 转函数
      }
      const args2 = args[2]
      if (isVNodeOrVNodes(args2)) { // 第三个参数是结点或结点数组
        args[2] = () => args2
      }
    }
  }
}

/** 处理vue3中的属性与插槽 */
function handleAttrAndSlot (args) {
  const options = args[1] // 第二个参数
  if (options && typeof options === 'object' && !Array.isArray(options)) { // 属性存在且是对象
    args[1] = getRealAttr(options)
    if (isVue3 && 'scopedSlots' in options) { // 如果是vue3且定义了具名插槽
      args[2] = options.scopedSlots
    }
  }
}

// 因为约定是按vue2的属性规则写，所以vue3需要转换
/** vue3中需要转换的属性 */
const specialAttrs = ['attrs', 'props', 'domProps']
/** vue3中需要转换的事件 */
const specialEvents = ['on', 'nativeOn']
/** 处理vue2与vue3中传递的属性结构不同的问题，得到实际的属性对象 */
function getRealAttr (options) {
  if (isVue2) {
    return options
  } else if (isVue3) {
    return Object.keys(options).reduce((opt, key) => {
      const value = options[key]
      if (specialAttrs.includes(key)) { // 如果是特定属性
        if (typeof value === 'object') {
          // 遍历加入对象中的所有属性
          Object.keys(value).forEach(k => {
            opt[k] = value[k]
          })
        }
      } else if (specialEvents.includes(key)) { // 如果是特定事件
        if (typeof value === 'object') {
          // 遍历加入对象中的所有属性
          Object.keys(value).forEach(k => {
            const ek = `on${k.charAt(0).toUpperCase()}${k.substring(1)}`
            opt[ek] = value[k]
          })
        }
      } else { // vue2与vue3相同
        opt[key] = value
      }
      return opt
    }, {})
  }
}

/** 是否是vue3的结点或结点数组 */
function isVNodeOrVNodes (o) {
  return o !== null && o !== undefined && (typeof o !== 'object' || o.ctx !== undefined || Array.isArray(o))
}